package com.med.meddataintegrator.jwt.filter;


import com.med.meddataintegrator.common.basic.exception.ServiceException;
import com.med.meddataintegrator.common.basic.response.StatusCode;
import com.med.meddataintegrator.jwt.service.Impl.UserDetailServiceImpl;
import com.med.meddataintegrator.jwt.utils.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

/**
 * 处理用户校验的过滤器
 * 从请求头中获取token并校验
 *
 */
@Slf4j
public class TokenFilter extends OncePerRequestFilter {

    // 拿到请求头的key值 以便获取token
    @Value("${jwt.tokenHeaderKey}")
    private String TOKEN_HEADER_KEY;
    // 拿到Token的前缀
    @Value("${jwt.tokenPrefix}")
    private String TOKEN_PREFIX;


    // 此处是 实现了 UserDetailsService 的 UserDetailServiceImpl
    @Autowired
    private UserDetailServiceImpl userDetailService;

    @Autowired
    private JwtUtil jwtUtil;



    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //判断路径是否需要过滤

        log.info("请求 ==================" + request.getRequestURI());
        //TODO 这里需要优化 后期路径为/mdi/v1/admin/   登录请求为 /mdi/v1/user/login
        if (request.getRequestURI().startsWith("/mdi/v1/admin/")){
            //日志记载当前请求路径
            log.info("Token 过滤器 当前请求路径为：{}",request.getRequestURI());
            // 从请求头中拿到token
            String token = request.getHeader(TOKEN_HEADER_KEY);
            // 判断token是否为认证校验token
            if (token == null || !token.startsWith(TOKEN_PREFIX)){
                // 继续执行下一个过滤链
                log.error("请求头中没有token");
                filterChain.doFilter(request,response);
                throw new ServiceException(StatusCode.TOKEN_NULL);
            }
            log.info("Token: {}", token);

            // 从Token中解析到用户名(subject)
            String subject = jwtUtil.getSubjectByToken(token);
            if (subject == null){
                log.error("token中的subject为空");
                filterChain.doFilter(request,response);
                throw new ServiceException(StatusCode.TOKEN_NULL);
            }

            //使用userDetails的用户名从数据库中查询用户信息  这里在Service中进行了重写
            UserDetails userDetails = userDetailService.loadUserByUsername(subject);
            // 判断用户名是否为空 并且 authentication为空 为空的时候 将信息加载到框架中去
            if ( userDetails.getUsername() != null
                    && Objects.isNull(SecurityContextHolder.getContext().getAuthentication())){
                // 将用户信息存入 authentication，方便后续校验
                // 第二个参数表示用户的凭证（通常是密码），但在这种情况下传递的是 null。
                // 这是因为 JWT 认证机制下，一旦 Token 被验证通过后，不再需要原始的密码信息来进行身份验证。
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null,
                        userDetails.getAuthorities());
                // 给authentication设置更多的请求信息，给WebAuthenticationDetailsSource传入请求对象 然后传给authentication以便其获的request中的更多信息
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                //将authentication设置到SecurityContextHolder中的上下文对象中去，用于后续的认证和授权。
                log.info("用户名：{}",userDetails.getUsername());
                SecurityContextHolder.getContext().setAuthentication(authentication);

            }
        }
        //过滤
        filterChain.doFilter(request,response);
    }
}
